﻿using System;
using System.Collections.Generic;

using System.Text;
using System.Globalization;

namespace System.Globalization {
    public class XQCalendar : GregorianCalendar {
        #region
        /// <summary>
        /// Field used to get or set the year.
        /// </summary>
        public const int YEAR = 1;

        /// <summary>
        /// Field used to get or set the month.
        /// </summary>
        public const int MONTH = 2;

        /// <summary>
        /// Field used to get or set the day of the month.
        /// </summary>
        public const int DATE = 5;

        /// <summary>
        /// Field used to get or set the hour of the morning or afternoon.
        /// </summary>
        public const int HOUR = 10;

        /// <summary>
        /// Field used to get or set the minute within the hour.
        /// </summary>
        public const int MINUTE = 12;

        /// <summary>
        /// Field used to get or set the second within the minute.
        /// </summary>
        public const int SECOND = 13;

        /// <summary>
        /// Field used to get or set the millisecond within the second.
        /// </summary>
        public const int MILLISECOND = 14;

        public const int ZONE_OFFSET = 15;

        public const int ERA = 16;//TODO

        /// <summary>
        /// Field used to get or set the day of the year.
        /// </summary>
        public const int DAY_OF_YEAR = 4;

        /// <summary>
        /// Field used to get or set the day of the month.
        /// </summary>
        public const int DAY_OF_MONTH = 6;

        /// <summary>
        /// Field used to get or set the day of the week.
        /// </summary>
        public const int DAY_OF_WEEK = 7;

        /// <summary>
        /// Field used to get or set the hour of the day.
        /// </summary>
        public const int HOUR_OF_DAY = 11;

        /// <summary>
        /// Field used to get or set whether the HOUR is before or after noon.
        /// </summary>
        public const int AM_PM = 9;

        /// <summary>
        /// Field used to get or set the value of the AM_PM field which indicates the period of the day from midnight to just before noon.
        /// </summary>
        public const int AM = 0;

        /// <summary>
        /// Field used to get or set the value of the AM_PM field which indicates the period of the day from noon to just before midnight.
        /// </summary>
        public const int PM = 1;
        #endregion

        DateTime _dateTime = DateTime.Now;
        public long GetTimeInMillis() {
            return (long)(_dateTime.Ticks / 10000);
        }

        /// <summary>
        /// Sets the corresponding field in an specified calendar with the value given.
        /// If the specified calendar does not have exist in the hash table, it creates a 
        /// new instance of the calendar with the current date and time and then assings it 
        /// the new specified value.
        /// </summary>
        /// <param name="calendar">The calendar to set its date or time.</param>
        /// <param name="field">One of the fields that composes a date/time.</param>
        /// <param name="fieldValue">The value to be set.</param>
        public void Set(int field, int fieldValue) {

            System.DateTime tempDate = _dateTime;
            switch (field) {
                case DATE:
                    tempDate = tempDate.AddDays(fieldValue - tempDate.Day);
                    break;
                case HOUR:
                    tempDate = tempDate.AddHours(fieldValue - tempDate.Hour);
                    break;
                case MILLISECOND:
                    tempDate = tempDate.AddMilliseconds(fieldValue - tempDate.Millisecond);
                    break;
                case MINUTE:
                    tempDate = tempDate.AddMinutes(fieldValue - tempDate.Minute);
                    break;
                case MONTH:
                    //Month value is 0-based. e.g., 0 for January
                    tempDate = tempDate.AddMonths((fieldValue + 1) - tempDate.Month);
                    break;
                case SECOND:
                    tempDate = tempDate.AddSeconds(fieldValue - tempDate.Second);
                    break;
                case YEAR:
                    tempDate = tempDate.AddYears(fieldValue - tempDate.Year);
                    break;
                case DAY_OF_MONTH:
                    tempDate = tempDate.AddDays(fieldValue - tempDate.Day);
                    break;
                case DAY_OF_WEEK:
                    tempDate = tempDate.AddDays((fieldValue - 1) - (int)tempDate.DayOfWeek);
                    break;
                case DAY_OF_YEAR:
                    tempDate = tempDate.AddDays(fieldValue - tempDate.DayOfYear);
                    break;
                case HOUR_OF_DAY:
                    tempDate = tempDate.AddHours(fieldValue - tempDate.Hour);
                    break;
                default:
                    break;
            }
            _dateTime = tempDate;
        }

        public void Add(int field, int fieldValue) {

            System.DateTime tempDate = _dateTime;
            switch (field) {
                case DATE:
                    tempDate = tempDate.AddDays(fieldValue);
                    break;
                case HOUR:
                    tempDate = tempDate.AddHours(fieldValue);
                    break;
                case MILLISECOND:
                    tempDate = tempDate.AddMilliseconds(fieldValue);
                    break;
                case MINUTE:
                    tempDate = tempDate.AddMinutes(fieldValue);
                    break;
                case MONTH:
                    //Month value is 0-based. e.g., 0 for January
                    tempDate = tempDate.AddMonths(fieldValue);
                    break;
                case SECOND:
                    tempDate = tempDate.AddSeconds(fieldValue);
                    break;
                case YEAR:
                    tempDate = tempDate.AddYears(fieldValue);
                    break;
                case DAY_OF_MONTH:
                    tempDate = tempDate.AddDays(fieldValue);
                    break;
                case DAY_OF_WEEK:
                    tempDate = tempDate.AddDays(fieldValue);
                    break;
                case DAY_OF_YEAR:
                    tempDate = tempDate.AddDays(fieldValue);
                    break;
                case HOUR_OF_DAY:
                    tempDate = tempDate.AddHours(fieldValue);
                    break;
                default:
                    break;
            }
            _dateTime = tempDate;
        }

        /// <summary>
        /// Sets the corresponding date (day, month and year) to the calendar specified.
        /// If the calendar does not exist in the hash table, it creates a new instance and sets 
        /// its values.
        /// </summary>
        /// <param name="calendar">The calendar to set its date.</param>
        /// <param name="year">Integer value that represent the year.</param>
        /// <param name="month">Integer value that represent the month.</param>
        /// <param name="day">Integer value that represent the day.</param>
        public void Set(System.Globalization.XQCalendar calendar, int year, int month, int day) {
            this.Set(YEAR, year);
            this.Set(MONTH, month);
            this.Set(DATE, day);
        }

        /// <summary>
        /// Sets the corresponding date (day, month and year) and hour (hour and minute) 
        /// to the calendar specified.
        /// If the calendar does not exist in the hash table, it creates a new instance and sets 
        /// its values.
        /// </summary>
        /// <param name="calendar">The calendar to set its date and time.</param>
        /// <param name="year">Integer value that represent the year.</param>
        /// <param name="month">Integer value that represent the month.</param>
        /// <param name="day">Integer value that represent the day.</param>
        /// <param name="hour">Integer value that represent the hour.</param>
        /// <param name="minute">Integer value that represent the minutes.</param>
        public void Set(System.Globalization.XQCalendar calendar, int year, int month, int day, int hour, int minute) {
            this.Set(YEAR, year);
            this.Set(MONTH, month);
            this.Set(DATE, day);
            this.Set(HOUR, hour);
            this.Set(MINUTE, minute);
        }



        /// <summary>
        /// Sets the corresponding date (day, month and year) and hour (hour, minute and second) 
        /// to the calendar specified.
        /// If the calendar does not exist in the hash table, it creates a new instance and sets 
        /// its values.
        /// </summary>
        /// <param name="calendar">The calendar to set its date and time.</param>
        /// <param name="year">Integer value that represent the year.</param>
        /// <param name="month">Integer value that represent the month.</param>
        /// <param name="day">Integer value that represent the day.</param>
        /// <param name="hour">Integer value that represent the hour.</param>
        /// <param name="minute">Integer value that represent the minutes.</param>
        /// <param name="second">Integer value that represent the seconds.</param>
        public void Set(int year, int month, int day, int hour, int minute, int second) {
            this.Set(YEAR, year);
            this.Set(MONTH, month);
            this.Set(DATE, day);
            this.Set(HOUR, hour);
            this.Set(MINUTE, minute);
            this.Set(SECOND, second);

        }

        /// <summary>
        /// Gets the value represented by the field specified.
        /// </summary>
        /// <param name="calendar">The calendar to get its date or time.</param>
        /// <param name="field">One of the field that composes a date/time.</param>
        /// <returns>The integer value for the field given.</returns>
        public int Get(int field) {
            int tempHour;
            switch (field) {
                case DATE:
                    return _dateTime.Day;
                case HOUR:
                    tempHour = _dateTime.Hour;
                    return tempHour > 12 ? tempHour - 12 : tempHour;
                case MILLISECOND:
                    return _dateTime.Millisecond;
                case MINUTE:
                    return _dateTime.Minute;
                case MONTH:
                    //Month value is 0-based. e.g., 0 for January
                    return _dateTime.Month - 1;
                case SECOND:
                    return _dateTime.Second;
                case YEAR:
                    return _dateTime.Year;
                case DAY_OF_MONTH:
                    return _dateTime.Day;
                case DAY_OF_YEAR:
                    return (int)(_dateTime.DayOfYear);
                case DAY_OF_WEEK:
                    return (int)(_dateTime.DayOfWeek) + 1;
                case HOUR_OF_DAY:
                    return _dateTime.Hour;
                case AM_PM:
                    tempHour = _dateTime.Hour;
                    return tempHour > 12 ? PM : AM;
                case ERA:
                    return 1;//TODO
                default:
                    return 0;
            }

        }


        /// Gets the calendar current date and time.
        /// </summary>
        /// <param name="calendar">The calendar to get its current date and time.</param>
        /// <returns>A System.DateTime value that indicates the current date and time for the 
        /// calendar given.</returns>
        public System.DateTime GetDateTime() {
            return _dateTime;
        }

        /// <summary>
        /// Sets the specified System.DateTime value to the specified calendar.
        /// </summary>
        /// <param name="calendar">The calendar to set its date.</param>
        /// <param name="date">The System.DateTime value to set to the calendar.</param>
        public void SetDateTime(System.DateTime date) {
            _dateTime = date;
        }
        /// <summary>
        /// Sets the time in the specified calendar with the long value.
        /// </summary>
        /// <param name="calendar">The calendar to set its date and time.</param>
        /// <param name="milliseconds">A long value that indicates the milliseconds to be set to 
        /// the hour for the calendar.</param>
        public void SetTimeInMilliseconds(long milliseconds) {
            _dateTime = new System.DateTime(milliseconds * 10000);

        }

        /// <summary>
        /// Gets what the first day of the week is; e.g., Sunday in US, Monday in France.
        /// </summary>
        /// <param name="calendar">The calendar to get its first day of the week.</param>
        /// <returns>A System.DayOfWeek value indicating the first day of the week.</returns>
        public System.DayOfWeek GetFirstDayOfWeek(System.Globalization.Calendar calendar) {
            if (_dateTimeFormat == null) {
                _dateTimeFormat = new System.Globalization.DateTimeFormatInfo();
                _dateTimeFormat.FirstDayOfWeek = System.DayOfWeek.Sunday;
            }
            return _dateTimeFormat.FirstDayOfWeek;
        }
        /// <summary>
        /// Sets what the first day of the week is; e.g., Sunday in US, Monday in France.
        /// </summary>
        /// <param name="calendar">The calendar to set its first day of the week.</param>
        /// <param name="firstDayOfWeek">A System.DayOfWeek value indicating the first day of the week
        /// to be set.</param>
        public void SetFirstDayOfWeek(System.DayOfWeek firstDayOfWeek) {
            if (_dateTimeFormat == null)
                _dateTimeFormat = new System.Globalization.DateTimeFormatInfo();

            _dateTimeFormat.FirstDayOfWeek = firstDayOfWeek;
        }

        /// <summary>
        /// Removes the specified field from the calendar given.
        /// If the field does not exists in the calendar, the calendar is removed from the table.
        /// </summary>
        /// <param name="calendar">The calendar to remove the value from.</param>
        /// <param name="field">The field to be removed from the calendar.</param>
        public void Clear(int field) {
            this.Set(field, 0);
        }

        public void Clear() {
            this.SetTimeInMilliseconds(0);
        }
        public System.Globalization.DateTimeFormatInfo _dateTimeFormat;
    }
}
